package asteroids.model;
/**
 * ALS EEN SHIP NAAR NOWORLD GAAT MOETEN ALLE BULLETS VERWIJDERD WORDEN!!!!!!!!!
 */

import ModelExceptions.*;
import asteroids.Util;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;

public class Bullet extends CollisionObject{
	/**
	 * 
	 * @param xCoordinate
	 * @param yCoordinate
	 * @param xVelocity
	 * @param yVelocity
	 * @param radius
	 * @param ship
	 * @param density
	 * @throws OutOfBoundsException
	 * @throws IllegalRadiusException
	 * @throws IllegalDensityException
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)==true && isValidRadius(radius) == true && isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			new.getXCoordinate() == xCoordinate
	 * 			new.getYCoordinate() == yCoordinate
	 * 			new.getXVelocity() == xVelocity
	 * 			new.getYVelocity() == yVelocity
	 * 			new.getRadius() == radius
	 * 			new.getAngle() == angle
	 * 			new.getDensity() == density
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 */
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship,double density) throws OutOfBoundsException, IllegalRadiusException, IllegalDensityException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		//		setMinimumDensity(0);
		//		setMaximumDensity(Double.MAX_VALUE);
		if(isValidDensity(density)){
			this.density = density;
		}
		else {throw new IllegalDensityException("That is an invalid density");}  
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());
		ship.addAsBullet(this);


	}

	/**
	 * 
	 * @param xCoordinate
	 * @param yCoordinate
	 * @param xVelocity
	 * @param yVelocity
	 * @param radius
	 * @param ship
	 * @throws OutOfBoundsException
	 * @throws IllegalRadiusException
	 * @throws IllegalDensityException
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)==true && isValidRadius(radius) == true && isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			new.getXCoordinate() == xCoordinate
	 * 			new.getYCoordinate() == yCoordinate
	 * 			new.getXVelocity() == xVelocity
	 * 			new.getYVelocity() == yVelocity
	 * 			new.getRadius() == radius
	 * 			new.getAngle() == angle
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 */
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship) throws OutOfBoundsException, IllegalRadiusException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		//		setMinimumDensity(0);
		//		setMaximumDensity(Double.MAX_VALUE);
		this.density = getStandardDensity();
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());
		ship.addAsBullet(this);


	}
	@Basic
	public double getStandardBulletRadius() {
		return standardBulletRadius;
	}
	private final double standardBulletRadius = 3;

	/**
	 * 
	 * @return	||if(isValidObject()  && isInWorld()){
	 * 			||result == true}
	 */

	@Raw
	public boolean isValidBullet(){
		if(!isValidObject()  || !isInWorld())//&& getBulletSource()!= null && getBulletSource().hasAsBullet(this))
			return false;
		return true;
	}

	/**
	 *@post new.getMass() = Math.pow(radius, 3)*4/3*Math.PI*getDensity()
	 */
	@Override @Basic
	public void setMass(double radius){
		double calcMass = Math.pow(radius, 3)*4/3*Math.PI*getDensity();
		if(isValidMass(calcMass))
			this.mass = calcMass;
		else throw new IllegalMassException("The density and radius result in an invalid mass");
	}

	@Basic
	public Ship getShip(){
		return this.bulletSourceShip;
	}
	/**
	 * 
	 * @param ship	The ship you want to check
	 * @return	||if(ship.isValidObject() && ship.isInWorld())
	 * 			||result == true
	 */
	public boolean canHaveAsSourceShip(Ship ship){
		if(ship.isValidObject() && ship.isInWorld())
			return true;
		return false;
	}



	@Basic
	private double getDensity(){
		return this.density;
	}

	/**
	 * 
	 * @param density 	The density you want to check
	 * @return			||if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) || !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())|| Double.isNaN(density)){
	 * 					||result == true;}
	 */
	private boolean isValidDensity(double density) {
		if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) || !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())|| Double.isNaN(density)){
			return true;
		}
		return false;
	}
	@Basic
	private double getMaximumDensity(){
		return this.maximumDensity;
	}
	@Basic
	private double getMinimumDensity(){
		return this.minimumDensity;
	}
	//	@Basic
	//	private void setMaximumDensity(double maxDensity){
	//		this.maximumDensity = maxDensity;
	//	}
	//	@Basic
	//	private void setMinimumDensity(double minDensity){
	//		this.minimumDensity = minDensity;
	//	}
	@Basic
	private double getStandardDensity(){
		return this.standardDensity;
	}

	private final double minimumDensity = 0;
	private final double maximumDensity = Double.POSITIVE_INFINITY;
	private final double density;
	private final double standardDensity = 7.8*Math.pow(10, 12);



	/**
	 * Return the source of <code>bullet</code>.
	 */
	@Basic
	public Ship getBulletSource(){
		return this.bulletSourceShip;
	}

	private Ship bulletSourceShip;

	/**
	 * 
	 * @param collisionObject	The object with what this will react
	 * @post					||if(collisionObject.isBullet()){
	 *							||	this.destroy()
	 *							||collisionObject.destroy()}
	 *@post						||if(collisionObject.isAsteroid()){
	 *							||	collisionObject.destroy();
	 *							||	this.destroy();}
	 *@post						||if(collisionObject.isShip()){
								||	if(collisionObject != this.getBulletSource() && this.getBulletSource() != null){
								||		this.destroy();
								||		collisionObject.destroy();}
								||	else{this.destroy();}
		}
	 */
	@Override
	public void collisionReactionWith(CollisionObject collisionObject) {
		if(collisionObject.isBullet()){
			this.destroy();
			collisionObject.destroy();
		}
		if(collisionObject.isAsteroid()){
			collisionObject.destroy();
			this.destroy();
		}
		if(collisionObject.isShip()){
			if(collisionObject != this.getBulletSource() && this.getBulletSource() != null){
				this.destroy();
				collisionObject.destroy();
			}
			else{this.destroy();}
		}
	}
	/**
	 * 
	 * @post	||this.destroy()
	 * 			||this.getBulletSource() == null;
	 */
	public void shipDestroy(){
		if(!this.isDestroyed())
			super.destroy();
		bulletSourceShip = null;
	}
	/**
	 * 
	 * @post	||this.destroy();
	 * 			||this.getBulletSource().hasAsBullet(this) == false;
	 * 			||this.getBulletSource() == null;
	 */
	@Override
	public void destroy(){
		if(!this.isDestroyed())
			getBulletSource().removeAsBullet(this);
		bulletSourceShip = null;
		super.destroy();
	}

	@Override
	public String toString(){
		return "This is a bullet with \n" +
				"Homeship: \t: " +getBulletSource() +"\n" + 
				super.toString()+
				"\n";
	}
	@Basic
	public int getMaxNbOfBounces(){
		return this.maxNbOfBounces;
	}
	@Basic
	public void setMaxNbOfBounces(int max){
		this.maxNbOfBounces = max;
	}
	int maxNbOfBounces =  1;
	@Basic
	public int getNbOfBounces(){
		return this.nbOfBounces;
	}
	@Basic
	public void setNbOfBounces(int nbOfBounces){
		this.nbOfBounces = nbOfBounces;
	}
	/**
	 * 
	 * @post ||(new this).getNbOfBounces == this.getNbOfBounces() + 1
	 */
	public void incrementNbOfBounces(){
		this.nbOfBounces = getNbOfBounces() + 1;
	}
	int nbOfBounces = 0;

	@Override
	public CollisionObject clone() {
		return new Bullet(getXCoordinate(), getYCoordinate(), getXVelocity(), getYVelocity(), getRadius(), getBulletSource(), getDensity());
	}
	private boolean checkNbOfBounces(){
		if(this.getNbOfBounces() >= this.getMaxNbOfBounces())
			return true;
		return false;
	}
	@Override //TODO
	public void bounceOffBoundary(){ 
		if(!checkNbOfBounces()){
			setVelocityAfterBoundaryCollision();
			setPositionAfterBoundaryCollision();
			this.incrementNbOfBounces();
		}
		else this.destroy();
	}
}
